#!/usr/bin/env.python
# _*_ coding:utf-8 _*_
# # # # # # # # # # # # # # # # # # # #
#  题目:      支付模块序列化器           #
#  姓名:         李吉辉                #
#  时间:    2019年1月2日20:15:25       #
# # # # # # # # # # # # # # # # # # # #
from decimal import Decimal

from django.db import transaction
from django.utils import timezone
from django_redis import get_redis_connection
from rest_framework import serializers

from goods.models import SKU
from .models import OrderInfo, OrderGoods

#  service mysql restart  mysql重启命令
class CommitOrderSerializer(serializers.ModelSerializer):
    """保存订单序列化器"""

    # 元类
    class Meta:
        # 映射模型
        model = OrderInfo
        # order_id 输出
        # 'address', 'pay_method' 输入
        fields = ['order_id', 'address', 'pay_method']
        read_only_fields = ('order_id',)  # 指定输出
        # 追加 规则
        extra_kwargs = {
            'address': {
                'write_only': True,
                'required': True,
            },
            'pay_method': {
                'write_only': True,
                'required': True
            }
        }

    def create(self, validated_data):
        """重写此方法来存储订单表-->订单商品表, 四张表的操作要么一起成功, 要么一起失败"""
        # 获取当前保存订单时需要的信息
        # 获取user对象
        user = self.context['request'].user
        # 生成订单编号 (当前时间 + user_id)
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id
        # 获取validated_data它里面的address模型对象
        address = validated_data.get('address')  # 取前端反序列化后的字典中的地址
        #  # 取前端反序列化后的字典中的购买方式
        pay_method = validated_data.get('pay_method')
        # status = '待支付' if '如果支付方式是支付宝支付' else '待发货'
        status = (OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                  # 判断 支付方式是否为支付宝支付
                  if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                  else OrderInfo.ORDER_STATUS_ENUM['UNSEND'])

        # ****开启一个明显事件****
        with transaction.atomic():
            # 加上try,直接判断缩进中所有的代码,有报错,直接捕获
            try:
                # 创建一个事务保存点
                save_point = transaction.savepoint()
                # 保存订单基本信息 OrderInfo（一）
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    # 涉及金钱用 Decimal
                    total_amount=Decimal('0.00'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=status
                )

                # 获取购物车中的所有数据
                redis_conn = get_redis_connection('cart')
                redis_cart = redis_conn.hgetall('cart_%s' % user.id)
                redis_selected = redis_conn.smembers('selected_%s' % user.id)

                # 从redis读取购物车中被勾选的商品信息
                cart_dict = {}
                for sku_id_bytes in redis_selected:
                    cart_dict[int(sku_id_bytes)] = int(redis_cart[sku_id_bytes])

                # skus = SKU.objects.filter(id__in=cart_dict.keys())  # 尽量不要这样一下取出来,可能会有缓存问题

                # 遍历购物车中被勾选的商品信息
                for sku_id, sku_count in cart_dict.items():
                    while True:
                        # 获取sku对象
                        sku = SKU.objects.get(id=sku_id)

                        # 延时测试
                        # import time
                        # time.sleep(5)

                        # 先把sku的库存和销量取出
                        origin_stock = sku.stock
                        origin_sales = sku.sales


                        # 判断库存 
                        # 如果购买量 > 库存
                        if sku_count > sku.stock:
                            raise serializers.ValidationError('库存不足')

                        # 减少库存，增加销量 SKU 
                        # sku.stock -= sku_count
                        # sku.sales += sku_count
                        # sku.save()

                        # 计算新的库存和销量
                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count

                        result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock,
                                                                                          sales=new_sales)
                        # 如果result为0说明用户下单失败,跳过本次while True继续下一次循环
                        if result == 0:
                            continue

                        # 修改SPU销量
                        spu = sku.goods  # 获取spu模型对象
                        spu.sales += sku_count
                        spu.save()

                        # 保存订单商品信息 OrderGoods（多）
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price
                        )

                        # 累加计算总数量和总价
                        order.total_count = order.total_count + sku_count  # 总数量
                        order.total_amount = order.total_amount + (sku.price * sku_count)  # 总价

                        # 如果能执行到这里说明当前的sku买到了
                        break

                # 最后加入邮费和保存订单信息
                order.total_amount += order.freight
                order.save()
            except Exception:
                # 异常暴力回滚
                transaction.savepoint_rollback(save_point)
                raise
            else:
                # 成功支付, 提交事务
                transaction.savepoint_commit(save_point)

        # 清除购物车中已结算的商品
        # 创建管道
        pl = redis_conn.pipeline()
        # 删除 set中勾选对应哈希中存的商品
        pl.hdel('cart_%s' % user.id, *redis_selected)
        # 清空set中的数据
        pl.srem('selected_%s' % user.id, *redis_selected)
        # 管道执行
        pl.execute()

        # 返回
        return order



class CartSKUSerializer(serializers.ModelSerializer):
    """
    购物车商品数据序列化器
    """
    # SKU模型中没有count,自加字段
    count = serializers.IntegerField(label='数量')

    # 元类
    class Meta:
        # 映射的模型
        model = SKU
        # 需要的字段
        fields = ('id', 'name', 'default_image_url', 'price', 'count')


class OrderSettlementSerializer(serializers.Serializer):
    """
    订单结算数据序列化器
    """
    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)
    #         序列化关联字段
    skus = CartSKUSerializer(many=True)